;;;; -*-Emacs-Lisp-*- Enhancements to Kyle Jones' Adaptive Fill Package
;;;; Written by Eric Eide, last modified on September 23, 1993.
;;;; (C) Copyright 1992, 1993, Eric Eide and the University of Utah
;;;;
;;;; COPYRIGHT NOTICE
;;;;
;;;; This program is free software; you can redistribute it and/or modify it
;;;; under the terms of the GNU General Public License as published by the Free
;;;; Software Foundation; either version 1, or (at your option) any later
;;;; version.
;;;;
;;;; This program is distributed in the hope that it will be useful, but
;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;;;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
;;;; for more details.
;;;;
;;;; You should have received a copy of the GNU General Public License along
;;;; with this program; if not, write to the Free Software Foundation, Inc.,
;;;; 675 Mass Ave, Cambridge, MA 02139, USA.

;;;; AUTHORS
;;;;
;;;; This set of functions was written by Eric Eide (eeide@cs.utah.edu) but is
;;;; very heavily based on Kyle Jones' "filladapt" package and the standard GNU
;;;; Emacs code.
;;;;
;;;; Addresses:
;;;;
;;;;   Eric Eide (eeide@cs.utah.edu)
;;;;   University of Utah
;;;;   3190 Merrill Engineering Building
;;;;   Salt Lake City, Utah  84112
;;;;
;;;;   Kyle Jones (kyle@crystal.wonderworks.com)
;;;;
;;;; Kyle Jones' "filladapt" package is Copyright (C) 1989 by Kyle E. Jones.
;;;; It is distributed under the terms of the GNU General Public License.

;;;; LISP CODE DIRECTORY INFORMATION
;;;;
;;;; LCD Archive Entry:
;;;; fa-extras|Eric Eide|eeide@cs.utah.edu|
;;;; Enhancements to Kyle Jones' "filladapt"; hanging lists in Lisp comments|
;;;; September 23 1993||~/packages/fa-extras.el.Z|

;;;; SUMMARY
;;;;
;;;; This file enhances Kyle Jones' "filladapt" package to properly indent
;;;; hanging paragraphs in more circumstances.  The whitespace that insets a
;;;; hanging paragraph may now contain both spaces and TABs.  Hanging
;;;; paragraphs within Lisp comments are also handled correctly.  Examples:
;;;;
;;;;   ;; + This text is     ;; 1. This text is     ;; (a) This text is
;;;;   ;;   filled.          ;;    filled.          ;;     filled.
;;;;
;;;; This file redefines three functions from Kyle Jones' "filladapt" package:
;;;; filladapt-negate-string, filladapt-normal, and filladapt-hanging-list.
;;;; The first two are redefined only in order to make them compatible with
;;;; both GNU Emacs 18 and GNU Emacs 19 (and the various derivatives of these,
;;;; including Epoch and Lucid GNU Emacs).
;;;;
;;;; This file also redefines three standard GNU Emacs functions: indent-new-
;;;; comment-line (from the file "simple.el"), fill-region-as-paragraph (from
;;;; "fill.el"), and lisp-mode-variables (from "lisp-mode.el").  All required
;;;; slight modifications to better handle comments.  The new versions of these
;;;; functions should be compatible with both GNU Emacs 18 and GNU Emacs 19
;;;; (and the various derivatives of these).
;;;;
;;;; To use this file, simply load it after Kyle Jones' "filladapt" package has
;;;; been loaded.  Or, one can load "fa-extras" instead of "filladapt"; this
;;;; file will automatically load "filladapt" as necessary.

(require 'filladapt)
(provide 'fa-extras)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; Here are the global variable declarations.
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;
;; This new filladapt table contains three new entries: the ones that refer to
;; the function filladapt-hanging-list-in-lisp-comment.
;;
;; Also, the regular expressions associated with filladapt-supercite-included-
;; text, filladapt-hanging-list, and filladapt-normal are slightly modified in
;; order to better match whitespace, and to be compatible with both Emacs 18
;; and 19.
;;

(setq filladapt-prefix-table
  '(
    ;; Included text in news or mail replies
    ("[ \t]*\\(>+ *\\)+" . filladapt-normal-included-text)
    ;; Included text generated by SUPERCITE.  We can't hope to match all
    ;; the possible variations, your mileage may vary.
    ("[ \t]*[^'`\"< \t]*> *" . filladapt-supercite-included-text)
    ;; These are the new entries for handling hanging lists within Lisp
    ;; comments.
    ("\\([ \t]*\\(\\(;[ \t]*\\)*\\);\\)[ \t]*(?\\([0-9]+[a-z]?\\|[a-z]\\))[ \t]+" .
     filladapt-hanging-list-in-lisp-comment)
    ("\\([ \t]*\\(\\(;[ \t]*\\)*\\);\\)[ \t]*\\([0-9]+[a-z]?\\|[a-z]\\)\\.[ \t]+" .
     filladapt-hanging-list-in-lisp-comment)
    ("\\([ \t]*\\(\\(;[ \t]*\\)*\\);\\)[-?!~*+ \t]*[ \t]" . ;; See NOTE below.
     filladapt-hanging-list-in-lisp-comment)
    ;; Lisp comments.  (ENE: I think that this is only called when there is no
    ;; whitespace between the semicolons and the comment text, as in ";;foo".
    ;; All other cases are caught by the previous regexp.)
    ("[ \t]*\\(;+[ \t]*\\)+" . filladapt-lisp-comment)
    ;; UNIX shell comments
    ("[ \t]*\\(#+[ \t]*\\)+" . filladapt-sh-comment)
    ;; Postscript comments
    ("[ \t]*\\(%+[ \t]*\\)+" . filladapt-postscript-comment)
    ;; C++ comments
    ("[ \t]*//[/ \t]*" . filladapt-c++-comment)
    ;; Lists with hanging indents, e.g.
    ;; 1. xxxxx   or   *   xxxxx   etc.
    ;;    xxxxx            xxx
    ("[ \t]*(?\\([0-9]+[a-z]?\\|[a-z]\\))[ \t]+" . filladapt-hanging-list)
    ("[ \t]*\\([0-9]+[a-z]?\\|[a-z]\\)\\.[ \t]+" . filladapt-hanging-list)
    ("[-?!~*+ \t]*[ \t]" . filladapt-hanging-list)
    ;; This keeps normal paragraphs from interacting unpleasantly with
    ;; the types given above.
    ("[^- \t/#%?!~*+]" . filladapt-normal)
    ))

;;
;; NOTE that the indicated regular expression for hanging lists in Lisp
;; comments sometimes does the "wrong thing" in unusual circumstances.  For
;; example, it will cause the following two lines to be filled "incorrectly":
;;
;;  ;; This is line one.
;;  ;; This is line two.
;;
;; The regular expression matches ";;  " and filladapt-hanging-list-in-lisp-
;; comment believes that it is the prefix.  The filler then fills the comments
;; as:
;;
;;  ;; This is line one.  ;; This is line two.
;;
;; (NOTE the embedded semicolons.)  However, the following lines will be filled
;; "correctly" because there is only one space between the semicolons:
;;
;; ;; This is line one.
;; ;; This is line two.
;;
;; These lines will be filled by filladapt-lisp-comment as:
;;
;; ;; This is line one.  This is line two.
;;
;; So be careful how you nest your semicolons.
;;

;;
;; Newer versions of GNU Emacs (FSF's Emacs 19 and Lucid GNU Emacs >= 19.8) can
;; restore old values of point from an undo stack, but older versions cannot.
;;

(defconst filladapt-undo-restores-point-p
  (condition-case error-data
      (progn
	;; This call to primitive-undo is a no-op if this version of GNU Emacs
	;; can restore old values of point from an undo stack.  But in versions
	;; of Emacs without the point-restoration ability, this call signals an
	;; error.
	(primitive-undo 1 (list (point)))
	t)
    (error nil))
  "A flag which indicates whether or not this version of GNU Emacs can restore
old values of point from an undo stack.")


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; These functions from Kyle Jones' "filladapt" package are redefined here in
;;;; order to be compatible with both GNU Emacs 18 and GNU Emacs 19.
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;
;;; The original version of this function contains a special case for `-'
;;; characters in the string; negated, they become "[^---]".  But that syntax
;;; is not valid in Emacs 19, and simply "[^-]" works in both 18.58 and 19.19,
;;; so there's no apparent need for a special case at all.
;;;

(defun filladapt-negate-string (string)
  (let ((len (length string))
	(i 0)
	string-list)
    (setq string-list (cons "\\(" nil))
    (while (< i len)
      (setq string-list
	    (cons (if (= i (1- len)) "" "\\|")
		  (cons "]"
			(cons (substring string i (1+ i))
			      ;; ENE: The above form used to be:
			      ;; (let ((str (substring string i (1+ i))))
			      ;;   (cond ((equal str "-") "---")
			      ;;         (t str)))
			      (cons "[^"
				    (cons (regexp-quote (substring string 0 i))
					  string-list)))))
	    i (1+ i)))
    (setq string-list (cons "\\)" string-list))
    (apply 'concat (nreverse string-list))))

;;;
;;; The original version of this function also uses the archaic "---" regexp
;;; construct.  The regexp has been rewritten to be compatible with both Emacs
;;; 18 and 19.
;;;

(defun filladapt-normal (paragraph)
  (if paragraph
      (setq paragraph-separate
	    (concat paragraph-separate "\\|^[- \t/#%?!~*+]"))))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; Here are the functions that allow one to fill hanging paragraphs.
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun filladapt-looking-at (regexp width)
  ;; This function returns t if point is at the beginning of the given regular
  ;; expression and the width (not length!) of the matching text is equal to
  ;; the specified width.
  (if (looking-at regexp)
      (let ((looking-at-width (- (save-excursion
				   (goto-char (match-end 0))
				   (current-column))
				 (if (bolp)
				     0
				   (current-column)))))
	(= looking-at-width width))
    nil))

(defun filladapt-make-whitespace (start-column width)
  ;; Make a string of whitespace containing TABs and spaces.
  (if indent-tabs-mode
      (let* ((end-column (+ start-column width))
	     (tabs-before-start (/ start-column tab-width))
	     (tabs-before-end (/ end-column tab-width))
	     (tabs (- tabs-before-end tabs-before-start))
	     (spaces (if (> tabs 0)
			 (% end-column tab-width)
		       width))
	     (whitespace (make-string (+ tabs spaces) ?\ )))
	;; Special case: don't use only one TAB to go one space.
	(if (and (= tabs 1) (= (+ tabs spaces) width))
	    (setq tabs 0
		  spaces (1+ spaces)))
	;; Fill in the whitespace string with the required TABs.
	(let ((index 0))
	  (while (> tabs 0)
	    (aset whitespace index ?\t)
	    (setq index (1+ index)
		  tabs (1- tabs))))
	whitespace)
    ;; indent-tabs-mode is nil; only use spaces.
    (make-string width ?\ )))

;;;
;;; The function below is a replacement for the function filladapt-hanging-list
;;; from Kyle Jones' "filladapt" package.  This new filladapt-hanging-list
;;; understands whitespace that contains both spaces and TABs.
;;;

(defun filladapt-hanging-list (paragraph)
  (let (prefix match match-width beg end)
    (setq match-width (save-excursion (goto-char (match-end 0))
				      (current-column)))
    (setq prefix (filladapt-make-whitespace 0 match-width))
    (if paragraph
	(progn
	  (setq match (buffer-substring (match-beginning 0) (match-end 0)))
	  (if (string-match "^[ \t]+$" match)
	      (save-excursion
		(while (and (not (bobp))
			    (filladapt-looking-at "[ \t]*" match-width))
		  (forward-line -1))
		(if (or (filladapt-looking-at
			 "[ \t]*(?\\([0-9]+[a-z]?\\|[a-z]\\))[ \t]+"
			 match-width)
			(filladapt-looking-at
			 "[ \t]*\\([0-9]+[a-z]?\\|[a-z]\\)\\.[ \t]+"
			 match-width)
			(filladapt-looking-at
			 "[-?!~*+ \t]*[ \t]"
			 match-width))
		    (setq beg (point))
		  (setq beg (progn (forward-line 1) (point)))))
	    (setq beg (point)))
	  (save-excursion
	    (goto-char beg)
	    (forward-line)
	    (while (filladapt-looking-at "[ \t]*" match-width)
	      (replace-match prefix nil t)
	      (forward-line))
	    (setq end (point)))
	  (narrow-to-region beg end)))
    (setq fill-prefix prefix)))

;;;
;;; filladapt-hanging-list-in-lisp-comment started as a merger of the functions
;;; filladapt-lisp-comment and filladapt-hanging-list (both from Kyle Jones'
;;; "filladapt" package).  But it became much more complicated when I taught it
;;; to understand whitespace containing both spaces and TABs.
;;;

(defun filladapt-hanging-list-in-lisp-comment (paragraph)
  (let (prefix match
	match-width		  ;; The width of the entire match.
	leading-whitespace-width  ;; ...of the whitespace before the semicolons
	to-last-semicolon-width	  ;; ...of the text between bol and the last ;.
	comment-semicolons	  ;; The text from the first ; to the last ;.
	matching-prefix-regexp	  ;; Regexp for finding prefixes "like" prefix.
	beg end)
    (save-excursion
      (setq match-width (progn (goto-char (match-end 0))
			       (current-column))
	    leading-whitespace-width (progn (goto-char (match-beginning 2))
					    (current-column))
	    to-last-semicolon-width (progn (goto-char (match-end 1))
					   (current-column))
	    comment-semicolons (buffer-substring (match-beginning 2)
						 (match-end 1))))
    (setq prefix
	  (concat
	   (filladapt-make-whitespace 0 leading-whitespace-width)
	   comment-semicolons
	   (filladapt-make-whitespace to-last-semicolon-width
				      (- match-width to-last-semicolon-width))
	   ))
    (if paragraph
	(progn
	  (setq paragraph-separate (concat "^" prefix "[ \t]*;\\|^"
					   (filladapt-negate-string prefix))
		match (buffer-substring (match-beginning 0) (match-end 0))
		matching-prefix-regexp (concat "[ \t]*"
					       (regexp-quote
						comment-semicolons)
					       "[ \t]*"))
	  (if (string-match "^[ \t]*\\(\\(;[ \t]*\\)*\\);[ \t]+$" match)
	      (save-excursion
		(while (and (not (bobp))
			    ;; I need two regular expressions here because I
			    ;; want the leading whitespace to be of a certain
			    ;; width.  I can't check that without two calls to
			    ;; filladapt-looking-prefix -- unless I change that
			    ;; function to accept more arguments.
			    (filladapt-looking-at "[ \t]*"
						  leading-whitespace-width)
			    (filladapt-looking-at matching-prefix-regexp
						  match-width))
		  (forward-line -1))
		(if (or
		     (filladapt-looking-at
		      "\\([ \t]*\\(\\(;[ \t]*\\)*\\);\\)[ \t]*(?\\([0-9]+[a-z]?\\|[a-z]\\))[ \t]+"
		      match-width)
		     (filladapt-looking-at
		      "\\([ \t]*\\(\\(;[ \t]*\\)*\\);\\)[ \t]*\\([0-9]+[a-z]?\\|[a-z]\\)\\.[ \t]+"
		      match-width)
		     (filladapt-looking-at
		      "\\([ \t]*\\(\\(;[ \t]*\\)*\\);\\)[-?!~*+ \t]*[ \t]"
		      match-width))
		    (setq beg (point))
		  (setq beg (progn (forward-line 1) (point)))))
	    (setq beg (point)))
	  (save-excursion
	    (goto-char beg)
	    (forward-line)
	    (while (and (filladapt-looking-at "[ \t]*"
					      leading-whitespace-width)
			(filladapt-looking-at matching-prefix-regexp
					      match-width))
	      (replace-match prefix nil t)
	      (forward-line))
	    (setq end (point)))
	  (narrow-to-region beg end)))
    (setq fill-prefix prefix)))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;
;;;; Here are the new versions of the standard GNU Emacs functions indent-new-
;;;; comment-line, fill-region-as-paragraph, and lisp-mode-variables.
;;;;
;;;; The functions below are based on their standard counterparts in GNU Emacs
;;;; 18.58 and GNU Emacs 19.19, with minor modifications in order to better
;;;; handle comments.  The functions below should be compatible with both Emacs
;;;; 18 and Emacs 19 (and the various derivatives of these, including Epoch and
;;;; Lucid GNU Emacs).
;;;;
;;;; I've annotated my changes with my initials (ENE).
;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;
;;; This version of indent-new-comment-line (originally from "simple.el") has
;;; been modified to work better when a fill prefix has been specified for
;;; filling a comment.  See the "changed this test" comment below.
;;;

(defun indent-new-comment-line ()
  "Break line at point and indent, continuing comment if presently within one.
The body of the continued comment is indented under the previous comment line."
  (interactive "*")
  (let (comcol comstart)
    (skip-chars-backward " \t")
    (delete-region (point)
		   (progn (skip-chars-forward " \t")
			  (point)))
    (insert ?\n)
    ;; ENE: In the standard Emacs 19 version of indent-new-comment-line, this
    ;; (save-excursion ...) form is executed only when comment-multi-line is
    ;; nil.  But the standard Emacs 18 version always executes this form.  So
    ;; in order to be compatible with both versions, I've gone with the Emacs
    ;; 18 technique.
    (save-excursion
      (if (and comment-start-skip
	       (let ((opoint (point)))
		 (forward-line -1)
		 (re-search-forward comment-start-skip opoint t)))
	  ;; The old line is a comment.
	  ;; Set WIN to the pos of the comment-start.
	  ;; But if the comment is empty, look at preceding lines
	  ;; to find one that has a nonempty comment.
	  (let ((win (match-beginning 0)))
	    (while (and (eolp) (not (bobp))
			(let (opoint)
			  (beginning-of-line)
			  (setq opoint (point))
			  (forward-line -1)
			  (re-search-forward comment-start-skip opoint t)))
	      (setq win (match-beginning 0)))
	    ;; Indent this line like what we found.
	    (goto-char win)
	    (setq comcol (current-column))
	    (setq comstart (buffer-substring (point) (match-end 0))))))
    (if (and comcol (not fill-prefix)) ;; ENE: Changed this test.
	(let ((comment-column comcol)
	      (comment-start comstart)
	      (comment-end comment-end))
	  (and comment-end (not (equal comment-end ""))
	       ;; ENE: In the standard Emacs 19 version of this command, the
	       ;; test for (not comment-multi-line) is commented out, along
	       ;; with the "else" clause of the "if".  As above, I've opted for
	       ;; Emacs 18 compatibility again.
	       (if (not comment-multi-line)
		   (progn
		     (forward-char -1)
		     (insert comment-end)
		     (forward-char 1))
		 (setq comment-column (+ comment-column (length comment-start))
		       comment-start "")))
	  (if (not (eolp))
	      (setq comment-end ""))
	  (insert ?\n)
	  (forward-char -1)
	  (indent-for-comment)
	  ;; ENE: The following (save-excursion ...) is from Emacs 19.  The
	  ;; Emacs 18 version of this command just does (delete-char 1).
	  (save-excursion
	    ;; Make sure we delete the newline inserted above.
	    (end-of-line)
	    (delete-char 1)))
      (if fill-prefix
	  (insert fill-prefix)
	(indent-according-to-mode)))))

;;;
;;; This version of fill-region-as-paragraph (originally from "fill.el") has
;;; been modified to skip past the comment start (if present) on the first line
;;; of the paragraph to be filled.  The added form is marked with my initials
;;; (ENE).
;;;
;;; NOTE that the standard Emacs 18 and Emacs 19 versions of this command are
;;; very different, and that the Emacs 19 version is generally smarter.  The
;;; version here is almost verbatim from GNU Emacs 19.19.
;;;

;; Adaptive Fill mode is new with Emacs 19, so we include the declarations here
;; to be compatible with Emacs 18.  GNU Emacs 19 enables adaptive-fill-mode by
;; default, but we set it to nil here -- we're using filladapt!

(defconst adaptive-fill-mode nil
  "*Non-nil means determine a paragraph's fill prefix from its text.")

(defconst adaptive-fill-regexp "[ \t]*\\([>*] +\\)?"
  "*Regexp to match text at start of line that constitutes indentation.
If Adaptive Fill mode is enabled, whatever text matches this pattern
on the second line of a paragraph is used as the standard indentation
for the paragraph.")

(defun fill-region-as-paragraph (from to &optional justify-flag)
  "Fill region as one paragraph: break lines to fit fill-column.
Prefix arg means justify too.
From program, pass args FROM, TO and JUSTIFY-FLAG."
  (interactive "r\nP")
  ;; Arrange for undoing the fill to restore point.
  ;;
  ;; ENE: Older versions of Emacs can't restore old values of point from the
  ;; buffer-undo-list.  See the comments for filladapt-undo-restores-point-p,
  ;; above.
  (if (and filladapt-undo-restores-point-p
	   buffer-undo-list
	   (not (eq buffer-undo-list t)))
      (setq buffer-undo-list (cons (point) buffer-undo-list)))
  ;; Don't let Adaptive Fill mode alter the fill prefix permanently.
  (let ((fill-prefix fill-prefix))
    ;; Figure out how this paragraph is indented, if desired.
    (if (and adaptive-fill-mode
	     (or (null fill-prefix) (string= fill-prefix "")))
	(save-excursion
	  (goto-char (min from to))
	  (if (eolp) (forward-line 1))
	  (forward-line 1)
	  (if (< (point) (max from to))
	      (let ((start (point)))
		(re-search-forward adaptive-fill-regexp)
		(setq fill-prefix (buffer-substring start (point))))
	    (goto-char (min from to))
	    (if (eolp) (forward-line 1))
	    ;; If paragraph has only one line, don't assume
	    ;; that additional lines would have the same starting
	    ;; decoration.  Assume no indentation.
;;	    (re-search-forward adaptive-fill-regexp)
;;	    (setq fill-prefix (make-string (current-column) ?\ ))
	    )))

    (save-restriction
      (narrow-to-region from to)
      (goto-char (point-min))
      (skip-chars-forward "\n")
      (narrow-to-region (point) (point-max))
      (setq from (point))
      (goto-char (point-max))
      (let ((fpre (and fill-prefix (not (equal fill-prefix ""))
		       (regexp-quote fill-prefix))))
	;; Delete the fill prefix from every line except the first.
	;; The first line may not even have a fill prefix.
	(and fpre
	     (progn
	       (if (>= (length fill-prefix) fill-column)
		   (error "fill-prefix too long for specified width"))
	       (goto-char (point-min))
	       (forward-line 1)
	       (while (not (eobp))
		 (if (looking-at fpre)
		     (delete-region (point) (match-end 0)))
		 (forward-line 1))
	       (goto-char (point-min))
	       (and (looking-at fpre) (forward-char (length fill-prefix)))
	       (setq from (point)))))
      ;; from is now before the text to fill,
      ;; but after any fill prefix on the first line.

      ;; Make sure sentences ending at end of line get an extra space.
      ;; loses on split abbrevs ("Mr.\nSmith")
      (goto-char from)
      (while (re-search-forward "[.?!][])}\"']*$" nil t)
	(insert ? ))

      ;; Then change all newlines to spaces.
      (subst-char-in-region from (point-max) ?\n ?\ )

      ;; Flush excess spaces, except in the paragraph indentation.
      (goto-char from)
      (skip-chars-forward " \t")
      ;; ENE: Added the following "if" form to move past the comment start.
      (if (and comment-start-skip
	       (looking-at comment-start-skip))
	  (goto-char (match-end 0)))
      ;; nuke tabs while we're at it; they get screwed up in a fill.  this is
      ;; quick, but loses when a sole tab follows the end of a sentence.
      ;; actually, it is difficult to tell that from "Mr.\tSmith".  blame the
      ;; typist.
      (subst-char-in-region (point) (point-max) ?\t ?\ )
      (while (re-search-forward "   *" nil t)
	(delete-region
	 (+ (match-beginning 0)
	    (if (save-excursion
		  (skip-chars-backward " ]})\"'")
		  (memq (preceding-char) '(?. ?? ?!)))
		2 1))
	 (match-end 0)))
      (goto-char (point-max))
      (delete-horizontal-space)
      (insert "  ")
      (goto-char (point-min))

      ;; This is the actual filling loop.
      (let ((prefixcol 0) linebeg)
	(while (not (eobp))
	  (setq linebeg (point))
	  (move-to-column (1+ fill-column))
	  (if (eobp)
	      nil
	    ;; Move back to start of word.
	    (skip-chars-backward "^ \n" linebeg)
	    ;; Don't break after a period followed by just one space.
	    ;; Move back to the previous place to break.
	    ;; The reason is that if a period ends up at the end of a line,
	    ;; further fills will assume it ends a sentence.
	    ;; If we now know it does not end a sentence,
	    ;; avoid putting it at the end of the line.
	    (while (and (> (point) (+ linebeg 2))
			(eq (preceding-char) ?\ )
			(not (eq (following-char) ?\ ))
			(eq (char-after (- (point) 2)) ?\.))
	      (forward-char -2)
	      (skip-chars-backward "^ \n" linebeg))
	    (if (if (zerop prefixcol)
		    (save-excursion
		      (skip-chars-backward " " linebeg)
		      (bolp))
		  (>= prefixcol (current-column)))
		;; Keep at least one word even if fill prefix exceeds margin.
		;; This handles all but the first line of the paragraph.
		;; Meanwhile, don't stop at a period followed by one space.
		(let ((first t))
		  (move-to-column prefixcol)
		  (while (and (not (eobp))
			      (or first
				  (and (not (bobp))
				       (save-excursion (forward-char -1)
						       (looking-at "\\. ")))))
		    (skip-chars-forward " ")
		    (skip-chars-forward "^ \n")
		    (setq first nil)))
	      ;; Normally, move back over the single space between the words.
	      (forward-char -1))
	    (if (and fill-prefix (zerop prefixcol)
		     (< (- (point) (point-min)) (length fill-prefix))
		     (string= (buffer-substring (point-min) (point))
			      (substring fill-prefix 0 (- (point) (point-min)))))
		;; Keep at least one word even if fill prefix exceeds margin.
		;; This handles the first line of the paragraph.
		;; Don't stop at a period followed by just one space.
		(let ((first t))
		  (while (and (not (eobp))
			      (or first
				  (and (not (bobp))
				       (save-excursion (forward-char -1)
						       (looking-at "\\. ")))))
		    (skip-chars-forward " ")
		    (skip-chars-forward "^ \n")
		    (setq first nil)))))
	  ;; Replace all whitespace here with one newline.
	  ;; Insert before deleting, so we don't forget which side of
	  ;; the whitespace point or markers used to be on.
	  (skip-chars-backward " ")
	  (insert ?\n)
	  (delete-horizontal-space)
	  ;; Insert the fill prefix at start of each line.
	  ;; Set prefixcol so whitespace in the prefix won't get lost.
	  (and (not (eobp)) fill-prefix (not (equal fill-prefix ""))
	       (progn
		 (insert fill-prefix)
		 (setq prefixcol (current-column))))
	  ;; Justify the line just ended, if desired.
	  (and justify-flag (not (eobp))
	       (progn
		 (forward-line -1)
		 (justify-current-line)
		 (forward-line 1))))))))

;;;
;;; This version of lisp-mode-variables (originally from "lisp-mode.el") has
;;; been modified to set comment-start-skip to ";+[ \t]*".  (Should it be set
;;; to ";[; \t]*" instead?  Simply ";+[ \t]*" seems to be good enough.)
;;;

(defun lisp-mode-variables (lisp-syntax)
  (cond (lisp-syntax
	 ;; ENE: The standard Emacs 19 version of this function assumes that
	 ;; the lisp-mode-syntax-table already exists.  The Emacs 18 version
	 ;; creates the table if necessary.  To be compatible with both
	 ;; versions, we use the version 18 code here.
	 (if (not lisp-mode-syntax-table)
	     (progn (setq lisp-mode-syntax-table
			  (copy-syntax-table emacs-lisp-mode-syntax-table))
		    (modify-syntax-entry ?\| "\"   " lisp-mode-syntax-table)
		    (modify-syntax-entry ?\[ "_   " lisp-mode-syntax-table)
		    (modify-syntax-entry ?\] "_   " lisp-mode-syntax-table)))
	 (set-syntax-table lisp-mode-syntax-table)))
  (setq local-abbrev-table lisp-mode-abbrev-table)
  (make-local-variable 'paragraph-start)
  (setq paragraph-start (concat "^$\\|" page-delimiter))
  (make-local-variable 'paragraph-separate)
  (setq paragraph-separate paragraph-start)
  (make-local-variable 'paragraph-ignore-fill-prefix)
  (setq paragraph-ignore-fill-prefix t)
  (make-local-variable 'indent-line-function)
  (setq indent-line-function 'lisp-indent-line)
  ;; ENE: The standard Emacs 18 version of this function does not set indent-
  ;; region-function.  In Emacs 18, lisp-indent-region does not exist.
  (if (fboundp 'lisp-indent-region)
      (progn
	(make-local-variable 'indent-region-function)
	(setq indent-region-function 'lisp-indent-region)
	))
  ;; ENE: The standard Emacs 18 version of this function does not set parse-
  ;; sexp-ignore-comments to t, because it doesn't work right in Emacs 18.  See
  ;; the documentation for parse-sexp-ignore-comments in Emacs 18.
  (make-local-variable 'parse-sexp-ignore-comments)
  (setq parse-sexp-ignore-comments
	(>= (string-to-int emacs-version) 19))
  (make-local-variable 'comment-start)
  (setq comment-start ";")
  (make-local-variable 'comment-start-skip)
  (setq comment-start-skip ";+[ \t]*") ;; ENE: Used to be ";+ *".
  (make-local-variable 'comment-column)
  (setq comment-column 40)
  ;; ENE: Emacs 18 uses the variable comment-indent-hook and Emacs 19 uses the
  ;; variable comment-indent-function.  To be compatible with both versions, we
  ;; set both variables.
  (make-local-variable 'comment-indent-hook)
  (setq comment-indent-hook 'lisp-comment-indent)
  (make-local-variable 'comment-indent-function)
  (setq comment-indent-function 'lisp-comment-indent))

;; End of file.

